
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** @author  John Miller
 *  @version 1.0
 *  @date    Sat Jan  5 14:10:45 EST 2013
 *  @see     LICENSE (MIT style license file).
 *  @see     http://reference.wolfram.com/mathematica/tutorial/ConstrainedOptimizationLocalNumerical.html
 *  @compile scalac -cp ../../classes -d classes PortfolioExOpt.scala
 *  @run     scala -cp ../../classes:classes simopt.PortfolioExOpt
 */

package optimization

import math.{abs, sqrt}

import scalation.linalgebra.{MatrixD, VectorD}
import scalation.stat.StatVector
import scalation.minima.ConjGradient

//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/** This object solves the following Quadratic Programming (QP) problem:
 *  Given n investment instruments, find a combination/portfolio that
 *  minimizes risk for a given expected return.
 */
object PortfolioExOpt extends App
{
     val label = Array ("3m Tbill", "long Tbond", "SP500", "Wilt.5000", "Corp. Bond", "NASDQ", "EAFE", "Gold")

     val r =  new MatrixD ((22, 8),
	1.075,	0.942,	0.852,	0.815,	0.698,	1.023,	0.851,	1.677,
	1.084,	1.02,	0.735,	0.716,	0.662,	1.002,	0.768,	1.722,
	1.061,	1.056,	1.371,	1.385,	1.318,	0.123,	1.354,	0.76,
	1.052,	1.175,	1.236,	1.266,	1.28,	1.156,	1.025,	0.96,
	1.055,	1.002,	0.926,	0.974,	1.093,	1.03,	1.181,	1.2,
	1.077,	0.982,	1.064,	1.093,	1.146,	1.012,	1.326,	1.295,
	1.109,	0.978,	1.184,	1.256,	1.307,	1.023,	1.048,	2.212,
	1.127,	0.947,	1.323,	1.337,	1.367,	1.031,	1.226,	1.296,
	1.156,	1.003,	0.949,	0.963,	0.99,	1.073,	0.977,	0.688,
	1.117,	1.465,	1.215,	1.187,	1.213,	1.311,	0.981,	1.084,
	1.092,	0.985,	1.224,	1.235,	1.217,	1.08,	1.237,	0.872,
	1.103,	1.159,	1.061,	1.03,	0.903,	1.15,	1.074,	0.825,
	1.08,	1.366,	1.316,	1.326,	1.333,	1.213,	1.562,	1.006,
	1.063,	1.309,	1.186,	1.161,	1.086,	1.156,	1.694,	1.216,
	1.061,	0.925,	1.052,	1.023,	0.959,	1.023,	1.246,	1.244,
	1.071,	1.086,	1.165,	1.179,	1.165,	1.076,	1.283,	0.861,
	1.087,	1.212,	1.316,	1.292,	1.204,	1.142,	1.105,	0.977,
	1.08,	1.054,	0.968,	0.938,	0.83,	1.083,	0.766,	0.922,
	1.057,	1.193,	1.304,	1.342,	1.594,	1.161,	1.121,	0.958,
	1.036,	1.079,	1.076,	1.09,	1.174,	1.076,	0.878,	0.926,
	1.031,	1.217,	1.1,	1.113,	1.162,	1.11,	1.326,	1.146,
	1.045,	0.889,	1.012,	0.999,	0.968,	0.965,	1.078,	0.99)

/*
	90,  	60,  	90, 	
	90, 	90, 	30,
	60, 	60, 	60,
	60, 	60,	90,
	30, 	30, 	30)
*/

    val mean = new VectorD (r.dim2)
    for (j <- 0 until r.dim2) mean(j) = r.col(j).sum / r.dim1
    val cova = new MatrixD (r.dim2, r.dim2)
    for (j <- 0 until r.dim2) {
        val x = new StatVector (r.col(j))
        for (k <- 0 until r.dim2) cova (j, k) = x.cov (r.col(k))
    } // for
    println ("mean = " + mean)
    println ("cova = " + cova)

    val x0 = new VectorD (r.dim2); x0.set (1. / r.dim2)
//  def f (x: VectorD) = (cova * x) dot x
//  def g (x: VectorD) = abs((mean dot x) - 1.25) + abs(x.sum - 1.) 

    def f (x: VectorD) = -(mean dot x) / sqrt((cova * x) dot x) 
    def g (x: VectorD) = (x.sum - 1.) + (1. - x.sum)

    val solver = new ConjGradient (f, g, false)
    var x = solver.solve (x0)

    println ("optimal solution x = " + x + " with an objective value f(x) = " + f(x))

    //average	1.078	1.093	1.120	1.124	1.121	1.046	1.141 	1.130

} // PortfolioExOpt

